﻿using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using Aragorn.SOA.ORM.Lambda.Builder;
using Aragorn.SOA.ORM.Lambda.Resolver;
using Aragorn.SOA.ORM.Lambda.ValueObjects;
using Dapper;
using Aragorn.SOA.ORM.Lambda.Builder;
using Aragorn.SOA.ORM.Lambda.Resolver;

namespace Aragorn.SOA.ORM.Lambda
{
    internal class SqlLam<T> : SqlLamBase
    {
        public SqlLam()
        {
            this._builder = new SqlQueryBuilder(LambdaResolver.GetTableName<T>(), SqlLamBase._defaultAdapter);
            this._resolver = new LambdaResolver(this._builder);
        }

        public SqlLam(Expression<Func<T, bool>> expression) : this()
        {
            this.Where(expression);
        }

        internal SqlLam(SqlQueryBuilder builder, LambdaResolver resolver)
        {
            this._builder = builder;
            this._resolver = resolver;
        }

        public SqlLam<T> And(Expression<Func<T, bool>> expression)
        {
            this._builder.And();
            this._resolver.ResolveQuery<T>(expression);
            return this;
        }

        public SqlLam<T> GroupBy(Expression<Func<T, object>> expression)
        {
            this._resolver.GroupBy<T>(expression);
            return this;
        }

        public SqlLam<TResult> Join<T2, TKey, TResult>(SqlLam<T2> joinQuery, Expression<Func<T, TKey>> primaryKeySelector, Expression<Func<T, TKey>> foreignKeySelector, Func<T, T2, TResult> selection)
        {
            SqlLam<TResult> sqlLam = new SqlLam<TResult>(this._builder, this._resolver);
            this._resolver.Join<T, T2, TKey>(primaryKeySelector, foreignKeySelector);
            return sqlLam;
        }

        public SqlLam<T2> Join<T2>(Expression<Func<T, T2, bool>> expression)
        {
            SqlLam<T2> sqlLam = new SqlLam<T2>(this._builder, this._resolver);
            this._resolver.Join<T, T2>(expression);
            return sqlLam;
        }

        public SqlLam<T> Or(Expression<Func<T, bool>> expression)
        {
            this._builder.Or();
            this._resolver.ResolveQuery<T>(expression);
            return this;
        }

        public SqlLam<T> OrderBy(Expression<Func<T, object>> expression)
        {
            this._resolver.OrderBy<T>(expression, false);
            return this;
        }

        public SqlLam<T> OrderByDescending(Expression<Func<T, object>> expression)
        {
            this._resolver.OrderBy<T>(expression, true);
            return this;
        }

        public SqlLam<T> Select(params Expression<Func<T, object>>[] expressions)
        {
            Expression<Func<T, object>>[] expressionArray = expressions;
            for (int i = 0; i < (int)expressionArray.Length; i++)
            {
                Expression<Func<T, object>> expression = expressionArray[i];
                this._resolver.Select<T>(expression);
            }
            return this;
        }

        public SqlLam<T> SelectAverage(Expression<Func<T, object>> expression)
        {
            this._resolver.SelectWithFunction<T>(expression, SelectFunction.AVG);
            return this;
        }

        public SqlLam<T> SelectCount(Expression<Func<T, object>> expression)
        {
            this._resolver.SelectWithFunction<T>(expression, SelectFunction.COUNT);
            return this;
        }

        public SqlLam<T> SelectDistinct(Expression<Func<T, object>> expression)
        {
            this._resolver.SelectWithFunction<T>(expression, SelectFunction.DISTINCT);
            return this;
        }

        public SqlLam<T> SelectMax(Expression<Func<T, object>> expression)
        {
            this._resolver.SelectWithFunction<T>(expression, SelectFunction.MAX);
            return this;
        }

        public SqlLam<T> SelectMin(Expression<Func<T, object>> expression)
        {
            this._resolver.SelectWithFunction<T>(expression, SelectFunction.MIN);
            return this;
        }

        public SqlLam<T> SelectSum(Expression<Func<T, object>> expression)
        {
            this._resolver.SelectWithFunction<T>(expression, SelectFunction.SUM);
            return this;
        }

        public SqlLam<T> Where(Expression<Func<T, bool>> expression)
        {
            return this.And(expression);
        }

        public SqlLam<T> WhereIsIn(Expression<Func<T, object>> expression, SqlLamBase sqlQuery)
        {
            this._builder.And();
            this._resolver.QueryByIsIn<T>(expression, sqlQuery);
            return this;
        }

        public SqlLam<T> WhereIsIn(Expression<Func<T, object>> expression, IEnumerable<object> values)
        {
            this._builder.And();
            this._resolver.QueryByIsIn<T>(expression, values);
            return this;
        }

        public SqlLam<T> WhereNotIn(Expression<Func<T, object>> expression, SqlLamBase sqlQuery)
        {
            this._builder.And();
            this._resolver.QueryByNotIn<T>(expression, sqlQuery);
            return this;
        }

        public SqlLam<T> WhereNotIn(Expression<Func<T, object>> expression, IEnumerable<object> values)
        {
            this._builder.And();
            this._resolver.QueryByNotIn<T>(expression, values);
            return this;
        }
    }
}